#region (c) 2014 THN Solutions LLC. - All rights reserved
/*
Simplified BSD License (BSD)
Copyright (c) 2014, THN Solutions LLC. ( www.ThnSolutions.com )
Author: Nguyễn, M. Hải                 ( www.Minh-Hai.com     )
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this 
      list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright notice, 
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/
#endregion

#region Record of Change
//             							CHANGE HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | DEVELOPER  | DESCRIPTION                                              |
// -------------------------------------------------------------------------------------
// | 25-Nov-09 | HaiNM      | First creation.                                          |
// -------------------------------------------------------------------------------------
//
#endregion

#region Record of Audit
//             							REVIEW HISTORY
// -------------------------------------------------------------------------------------
// |   DATE    | REVIEWER   | NOTES                                                    |
// -------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------
//
#endregion

#region Using directives
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.ComponentModel;
using System.Security.Permissions;
using System.Runtime.InteropServices;
using VS = System.Windows.Forms.VisualStyles;

using Thn;
using Thn.Interface.Vcl;
#endregion

namespace Thn.Interface.Vcl
{
    /*
     * This control was based on the work of: Lukasz Swiatkowski
     * http://lukesw.net/articles/SimplePopup.aspx
     */
    /// <summary>
    /// This is a helper class for showing popups.
    /// </summary>
    [CLSCompliant(true), ToolboxItem(false)]
#if DEBUG
    public
#else
    public
#endif
 partial class Popup : ControlBase
    {
        #region Fields

        private NativePopup mInner;
        #endregion

        #region Properties

        #region Content
        Control mContent = null;

        /// <summary>
        /// Gets/Sets the content of the pop-up.
        /// </summary>
        [Browsable(true), DefaultValue(null), Category("Data"), Description("Content of the popup")]
        public Control Content
        {
            get { return mContent; }
            set { mContent = value; }
        }
        #endregion

        #region Auto Close
        private bool mAutoClose = true;
        /// <summary>
        /// Gets/Sets whether the pop-up should close when it has lost its activation.
        /// </summary>
        [Browsable(true), DefaultValue(true), Category("Behavior"), Description("Whether the pop-up should close when it has lost its activation.")]
        public bool AutoClose
        {
            get { return mAutoClose; }
            set { mAutoClose = value; }
        }
        #endregion

        #region Showing Animation
        private PopupAnimations mShowingAnimation = PopupAnimations.SystemDefault;
        /// <summary>
        /// Gets/Sets animation style to apply while showing the pop-up windows.
        /// </summary>
        [Browsable(true), DefaultValue(PopupAnimations.SystemDefault), Category("Appearance"), Description("Animation style for showing the pop-up windows.")]
        public PopupAnimations ShowingAnimation
        {
            get { return mShowingAnimation; }
            set { mShowingAnimation = value; }
        }
        #endregion

        #region Hiding Animation
        private PopupAnimations mHidingAnimation = PopupAnimations.None;
        /// <summary>
        /// Gets/Sets animation style to apply while hiding the pop-up windows.
        /// </summary>
        [Browsable(true), DefaultValue(PopupAnimations.None), Category("Appearance"), Description("Animation style for hiding the pop-up windows.")]
        public PopupAnimations HidingAnimation
        {
            get { return mHidingAnimation; }
            set { mHidingAnimation = value; }
        }
        #endregion

        #region Animation Duration
        private int mAnimationDuration = 100;
        /// <summary>
        /// Gets/Sets the duration (in milliseconds) for animation effects.
        /// </summary>
        [Browsable(true), DefaultValue(100), Category("Appearance"), Description("Duration (in ms.) for animation effects.")]
        public int AnimationDuration
        {
            get { return mAnimationDuration; }
            set { mAnimationDuration = value; }
        }
        #endregion

        #region Focus On Open
        private bool mFocusOnOpen = true;
        /// <summary>
        /// Gets or sets a value indicating whether the content should receive the focus after the pop-up has been opened.
        /// </summary>
        /// <value><c>true</c> if the content should be focused after the pop-up has been opened; otherwise, <c>false</c>.</value>
        /// <remarks>If the FocusOnOpen property is set to <c>false</c>, then pop-up cannot use the fade effect.</remarks>
        [Browsable(true), DefaultValue(true), Category("Behavior"), Description("Whether content should receive focus when pop-up is opened.")]
        public bool FocusOnOpen
        {
            get { return mFocusOnOpen; }
            set { mFocusOnOpen = value; }
        }
        #endregion

        #region Accept Alt
        private bool mAcceptAlt = true;
        /// <summary>
        /// Gets or sets a value indicating whether presing the alt key should close the pop-up.
        /// </summary>
        /// <value><c>true</c> if presing the alt key does not close the pop-up; otherwise, <c>false</c>.</value>
        [Browsable(true), DefaultValue(true), Category("Behavior"), Description("Whether pressing the Alt key should close the pop-up.")]
        public bool AcceptAlt
        {
            get { return mAcceptAlt; }
            set { mAcceptAlt = value; }
        }
        #endregion

        #region Resizable
        private bool mResizable;
        /// <summary>
        /// Gets or sets a value indicating whether the <see cref="PopupControl.Popup" /> is resizable.
        /// </summary>
        /// <value><c>true</c> if resizable; otherwise, <c>false</c>.</value>
        [Browsable(true), DefaultValue(false), Category("Behavior"), Description("Whether the pop-up form is resizable.")]
        public bool Resizable
        {
            get { return mResizable; }
            set { mResizable = value; }
        }
        #endregion

        #region Minimum Size
        private Size mMinimumSize;
        /// <summary>
        /// Gets or sets a minimum size of the pop-up.
        /// </summary>
        /// <returns>An ordered pair of type <see cref="T:System.Drawing.Size" /> representing the width and height of a rectangle.</returns>
        [Browsable(true), Category("Appearance"), Description("Minimum size of the pop-up.")]
        public new Size MinimumSize
        {
            get { return mMinimumSize; }
            set { mMinimumSize = value; }
        }
        #endregion

        #region Maximum Size
        private Size mMaximumSize;
        /// <summary>
        /// Gets or sets a maximum size of the pop-up.
        /// </summary>
        /// <returns>An ordered pair of type <see cref="T:System.Drawing.Size" /> representing the width and height of a rectangle.</returns>
        [Browsable(true), Category("Appearance"), Description("Maximum size of the pop-up.")]
        public new Size MaximumSize
        {
            get { return mMaximumSize; }
            set { mMaximumSize = value; }
        }
        #endregion

        #region Is Showing
        private bool mIsShowing;
        /// <summary>
        /// Checks whether the pop-up is being shown.
        /// </summary>
        public bool IsShowing
        {
            get { return mIsShowing; }
        }
        #endregion

        #endregion

        #region Events

        #region Shown
        /// <summary>
        /// This event is raised after the pop-up form is shown.
        /// </summary>
        [Browsable(true), Category(""), Description("Occurs after the pop-up form is shown.")]
        public event EventHandler Shown;

        /// <summary>
        /// Invoke event Shown
        /// </summary>        
        protected void RaiseShown(EventArgs args)
        {
            EventHandler handler = Shown;
            if (handler != null) handler(this, args);
        }
        #endregion

        #region Accepted
        /// <summary>
        /// This event is raised after the pop-up form is closed with Accepted status.
        /// </summary>
        [Browsable(true), Category(""), Description("Occurs after the pop-up form is closed with Accepted status.")]
        public event EventHandler Accepted;

        /// <summary>
        /// Invoke event Accepted
        /// </summary>        
        protected void RaiseAccepted(EventArgs args)
        {
            EventHandler handler = Accepted;
            if (handler != null) handler(this, args);
        }
        #endregion

        #region Canceled
        /// <summary>
        /// This event is raised after the pop-up form is closed with Canceled status.
        /// </summary>
        [Browsable(true), Category(""), Description("Occurs after the pop-up form is closed with Canceled status.")]
        public event EventHandler Canceled;

        /// <summary>
        /// Invoke event Canceled
        /// </summary>        
        protected void RaiseCanceled(EventArgs args)
        {
            EventHandler handler = Canceled;
            if (handler != null) handler(this, args);
        }
        #endregion

        #endregion

        #region Show
        /// <summary>
        /// Shows the pop-up window below the specified control.
        /// </summary>
        /// <param name="control">The control below which the pop-up will be shown.</param>
        /// <remarks>
        /// When there is no space below the specified control, the pop-up control is shown above it.
        /// </remarks>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="control"/> is <code>null</code>.</exception>
        public void Show(Control control)
        {
            if (mContent != null)
            {
                #region Has content

                if (!mIsShowing)
                {
                    PrepareInner();
                    CopyProperties();
                    mInner.Show(control);
                }
                #region Defensive tracing
                else
                {
                    //already showing the pop-up.
                }
                #endregion

                #endregion
            }
            else
            {
                #region No content yet
                throw new NullReferenceException("Content must be assigned");
                #endregion
            }
        }

        /// <summary>
        /// Shows the pop-up window below the specified area of the specified control.
        /// </summary>
        /// <param name="control">The control used to compute screen location of specified area.</param>
        /// <param name="area">The area of control below which the pop-up will be shown.</param>
        /// <remarks>
        /// When there is no space below specified area, the pop-up control is shown above it.
        /// </remarks>
        /// <exception cref="T:System.ArgumentNullException"><paramref name="control"/> is <code>null</code>.</exception>
        public void Show(Control control, Rectangle area)
        {
            if (mContent != null)
            {
                #region Has content

                if (!mIsShowing)
                {
                    PrepareInner();
                    CopyProperties();
                    mInner.Show(control, area);
                }
                #region Defensive tracing
                else
                {
                    //already showing the pop-up.
                }
                #endregion

                #endregion
            }
            else
            {
                #region No content yet
                throw new NullReferenceException("Content must be assigned");
                #endregion
            }
        }
        #endregion

        #region Accept
        /// <summary>
        /// Close the pop-up form if it is showing and raise event Accepted.
        /// </summary>
        public void Accept()
        {
            if (mIsShowing)
            {
                mInner.Close(ToolStripDropDownCloseReason.ItemClicked);
            }
            #region Defensive tracing
            else
            {
                //not showing yet
            }
            #endregion
        }
        #endregion

        #region Cancel
        /// <summary>
        /// Close the pop-up form if it is showing and raise event Canceled.
        /// </summary>
        public void Cancel()
        {
            if (mIsShowing)
            {
                mInner.Close(ToolStripDropDownCloseReason.CloseCalled);
            }
            #region Defensive tracing
            else
            {
                //not showing yet
            }
            #endregion
        }
        #endregion

        #region On Get Render Context
        /// <summary>
        /// Get reference to the renderation context which is used by <see cref="IControlRenderer"/>.
        /// </summary>
        protected override RenderContext OnGetRenderContext(Graphics canvas)
        {
            return null;
        }
        #endregion

        #region Prepare Inner
        void PrepareInner()
        {
            if (mInner == null)
            {
                mInner = new NativePopup(mContent);
                mInner.Opened += new EventHandler(mInner_Opened);
                mInner.Opening += new CancelEventHandler(mInner_Opening);
                mInner.Closed += new ToolStripDropDownClosedEventHandler(mInner_Closed);
                mInner.Closing += new ToolStripDropDownClosingEventHandler(mInner_Closing);
            }
            #region Defensive tracing
            else
            {
                //already instantiated.
            }
            #endregion
        }
        #endregion

        #region Inner Popup Events

        #region Closing
        void mInner_Closing(object sender, ToolStripDropDownClosingEventArgs e)
        {
            //Log.Debug("Closing - Cancel: {0} Reason: {1}", e.Cancel, e.CloseReason);
        }
        #endregion

        #region Closed
        void mInner_Closed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            mIsShowing = false;
            switch (e.CloseReason)
            {
                #region Item Clicked
                case ToolStripDropDownCloseReason.ItemClicked:
                    RaiseAccepted(EventArgs.Empty);
                    break;
                #endregion

                #region App Clicked
                case ToolStripDropDownCloseReason.AppClicked:
                    RaiseCanceled(EventArgs.Empty);
                    break;
                #endregion

                #region App Focus Change
                case ToolStripDropDownCloseReason.AppFocusChange:
                    RaiseCanceled(EventArgs.Empty);
                    break;
                #endregion

                #region Close Called
                case ToolStripDropDownCloseReason.CloseCalled:
                    RaiseCanceled(EventArgs.Empty);
                    break;
                #endregion

                #region Keyboard
                case ToolStripDropDownCloseReason.Keyboard:
                    RaiseCanceled(EventArgs.Empty);
                    break;
                #endregion

                #region Unsupported
                default:
                    throw new NotSupportedException(string.Format("Does not support CloseReason of {0}", e.CloseReason));
                    break;
                #endregion
            }
            //Log.Debug("Closed - Reason: {0}", e.CloseReason);
        }
        #endregion

        #region Opening
        void mInner_Opening(object sender, CancelEventArgs e)
        {
            //Log.Debug("Opening - Cancel: {0}", e.Cancel);
        }
        #endregion

        #region Opened
        void mInner_Opened(object sender, EventArgs e)
        {
            mIsShowing = true;
            RaiseShown(EventArgs.Empty);
        }
        #endregion

        #endregion

        #region Copy properties
        void CopyProperties()
        {
            mInner.ShowingAnimation = mShowingAnimation;
            mInner.HidingAnimation = mHidingAnimation;
            mInner.AnimationDuration = mAnimationDuration;
            mInner.FocusOnOpen = mFocusOnOpen;
            mInner.AcceptAlt = mAcceptAlt;
            mInner.Resizable = mResizable;
            mInner.MinimumSize = mMinimumSize;
            mInner.MaximumSize = mMaximumSize;
            mInner.AutoClose = mAutoClose;
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor.
        /// </summary>
        public Popup()
        {
        }
        #endregion
    }
}
